package org.cyhz.standardize.service;

import java.sql.SQLException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.cyhz.domain.AutoInfoVO;
import org.cyhz.domain.SbVO;
import org.cyhz.domain.SsVO;
import org.cyhz.domain.TimeWindow;
import org.cyhz.standardize.dao.CarInfoDao;
import org.cyhz.util.Log;
import org.cyhz.util.TimeWindowUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component("bSStandardizeStep2Service")
public class BSStandardizeStep2ServiceImp implements BSStandardizeStepService {

	@Autowired
	private CarInfoDao carInfoDao;

	@Value("#{configSettings['step2.time.window.width']}")
	private int timeWindowWidth;

	@Value("#{configSettings['other.brand']}")
	private String otherBrand;

	@Value("#{configSettings['other.series']}")
	private String otherSeries;

	@Value("#{configSettings['startdate']}")
	private String startDate;

	@Value("#{configSettings['enddate']}")
	private String endDate;

	@Value("#{configSettings['ultimate_check']}")
	private String ultimate_check;

	@Autowired
	private UltimateCheck ultimateCheck;

	@Autowired
	private SSeriesSearchService sSeriesSearchService;

	@Autowired
	private SBrandSearchService sBrandSearchService;

	public Date getStartDate() throws ParseException {
		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		return df.parse(this.startDate);
	}

	public Date getEndDate() throws ParseException {

		Date d = null;
		if (null == this.endDate || this.endDate.trim().equals("")) {
			d = new Date();
		} else {
			DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
			d = df.parse(this.endDate);
		}
		d = TimeWindowUtil.getLastSecondOfDay(d);
		return d;
	}

	@Override
	public void standardize() throws SQLException {

		Date startDate = null;
		Date endDate = null;
		try {
			startDate = this.getStartDate();
		} catch (ParseException e) {
			Log.error("get startDate error", e);
		}

		try {
			endDate = this.getEndDate();
		} catch (ParseException e) {
			Log.error("get endDate error", e);
		}

		List<TimeWindow> timeWindows = null;
		try {
			timeWindows = TimeWindowUtil.getContiguousTimeWindows(
					this.getStartDate(), this.getEndDate(), timeWindowWidth);
		} catch (ParseException e) {
			Log.error("build contiguous time window error", e);
		}

		if (null != timeWindows) {

			for (TimeWindow tw : timeWindows) {

				List<AutoInfoVO> ais = carInfoDao.getCarVoByNbNsAndReleaseTime(
						tw.getStartDate(), tw.getEndDate());

				for (AutoInfoVO ai : ais) {

					if (null == ai) {
						// dirty data
						continue;
					}

					Long now = new Date().getTime();
					Log.info("start..." + ai.toString());

					StandardizeResult sr = this.standardize(ai);

					// check sb ss in sbss mapping
					if (otherBrand.equals(sr.getSb())
							|| otherSeries.equals(sr.getSs())) {
						// has other , ignore
					} else {
						if (StandardizeResources.sbssnameConcatString
								.contains(sr.getSb() + sr.getSs())) {
							// sb ss in sbss , correct
						} else {
							Log.error("get sb ss " + sr.toString()
									+ " not in sbss mapping");
							sr.setSb(otherBrand);
							sr.setSs(otherSeries);
						}
					}

					// ultimate check

					if ("ON".equals(ultimate_check.toUpperCase())) {
						sr = ultimateCheck.check(sr, ai, AutoInfoUtil.getT(ai));
					}

					int effectRow = carInfoDao.updateSbSsByNbNsTC(
							sr.getSb(), sr.getSs(), ai.getBrand(),
							ai.getSeries(), ai.getTitle(), ai.getAutoName(),
							startDate, endDate);

					Log.info(ai.toString() + "--->" + sr.getSb() + "|"
							+ sr.getSs() + " effect " + effectRow + " cost"
							+ (new Date().getTime() - now));

				}
				Log.info("finish step2 " + tw.toString());
			}
		}
	}

	private StandardizeResult standardize(AutoInfoVO ai) {
		StandardizeResult sr = new StandardizeResult(otherBrand, otherSeries);
		String tc = AutoInfoUtil.getTC(ai);
		String nb = ai.getBrand();
		String ns = ai.getSeries();
		if (StandardizeResources.nbsbMapping.containsKey(nb)) {
			// find sblist by nb
			List<SbVO> sbvos = StandardizeResources.nbsbMapping.get(nb);
			if (null == sbvos || sbvos.isEmpty()) {
				// sbvos is null
				return sr;
			} else if (sbvos.size() == 1) {
				// unique sb
				sr.setSb(sbvos.get(0).getSbrand());

				// get ssList by sb and ns
				List<SsVO> ssvos = sSeriesSearchService.getSseriesListFromSN(
						sr.getSb(), ai.getSeries());

				if (null == ssvos || ssvos.isEmpty()) {
					// ssvos is null
					// sb is ok , but ss may get from tc and sb
					List<SbVO> sbs = new ArrayList<>();
					sbs.add(sbvos.get(0));
					ssvos = sSeriesSearchService.getSseriesList(tc,
							getNsListBySbList(sbs));

					return setSsFromSsvos(sr, ssvos);
				} else if (ssvos.size() == 1) {
					sr.setSs(ssvos.get(0).getSeries());
					return sr;
				} else {
					// sb is ok ,but get multi ss by sb and ns

					List<String> nss = getNsListBySbListSsList(sbvos, ssvos);
					ssvos = sSeriesSearchService.getSseriesList(tc, nss);
					return setSsFromSsvos(sr, ssvos);
				}
			}

		} else {
			Log.info("get null by nb,get ss by ns next");
			// do not find sblist by nb , next , to get ss by ns
			List<SsVO> ssvos = sSeriesSearchService.getSseriesListFromByNs(ns);

			if (null == ssvos || ssvos.isEmpty()) {
				// sb from nb is null , ss from ns is null , next to get from tc
				Log.info("get null by ns,get sb from tc");
				List<SbVO> sbvos = sBrandSearchService.getSBrandListFromTC(tc,
						null);

				if (null == sbvos || sbvos.isEmpty()) {
					// sb from tc is null , next get ss from tc
					Log.info("get null sb from tc ,get ss from tc next");
					ssvos = sSeriesSearchService.getSseriesList(tc, null);
					if (null == ssvos || ssvos.isEmpty()) {
						// null sb from tc , null ss from tc
						return sr;
					} else if (ssvos.size() == 1) {
						SsVO ssvo = ssvos.get(0);
						sr.setSs(ssvo.getSeries());
						sbvos = StandardizeResources.sssbMapping.get(ssvo);

						if (null == sbvos || sbvos.isEmpty()) {
							// null sb from tc , null sb from sssb , ss is ok
							return sr;
						} else if (sbvos.size() == 1) {
							// null sb from tc , unique sb from sssb , ss is ok
							sr.setSb(sbvos.get(0).getSbrand());
							return sr;
						} else {
							// null sb from tc,multi sb from sssb , ss is ok
							return sr;
						}

					} else {
						// null sb from tc , multi ss from tc
						return sr;
					}

				} else if (sbvos.size() == 1) {
					Log.info("get unique sb " + sbvos.get(0).getSbrand()
							+ " from tc ,get ss from tc by nss in sbnb next");
					sr.setSb(sbvos.get(0).getSbrand());
					// get sslist from tc by sb
					ssvos = sSeriesSearchService.getSseriesList(tc,
							this.getNsListBySbList(sbvos));

					if (null == ssvos || ssvos.isEmpty()) {
						// sb is ok , ss is null from tc
						return sr;
					} else if (ssvos.size() == 1) {
						// sb is ok , ss is ok
						sr.setSs(ssvos.get(0).getSeries());
					} else {
						// sb is ok , multi ss from tc
						return sr;
					}
				} else {
					// multi sb from tc,null ss from tc
					return sr;
				}

			} else if (ssvos.size() == 1) {
				// sb from nb is null , ss from ns is unique, next get sb by ss
				SsVO ssvo = ssvos.get(0);
				sr.setSs(ssvo.getSeries());

				if (StandardizeResources.sssbMapping.containsKey(ssvo)) {
					// sb list exists
					List<SbVO> sbvos = StandardizeResources.sssbMapping
							.get(ssvo);

					if (sbvos.size() == 1) {
						// ss is ok , sb from sssb mapping is unique
						sr.setSb(sbvos.get(0).getSbrand());
						return sr;
					} else {
						// ss is ok , sb from ss multi, next get sb from tc by
						// sbs , by ss
						sbvos = sBrandSearchService.getSBrandListFromTC(tc,
								this.getNbListBySbListSsList(sbvos, ssvos));

						if (null == sbvos || sbvos.isEmpty()) {
							// ss is ok , sb from tc is null
							return sr;
						} else if (sbvos.size() == 1) {
							// ss is ok , unique sb
							sr.setSb(sbvos.get(0).getSbrand());
							return sr;
						} else {
							// ss is ok , sb from tc is multi
							return sr;
						}

					}

				} else {
					// ss is ok , sb list from ss is null
					return sr;
				}

			} else {
				// sb from nb is null , ss from ns is multi,next get sb from tc
				List<String> nbs = this.getNbListBySsList(ssvos);
				List<SbVO> sbvos = sBrandSearchService.getSBrandListFromTC(tc,
						nbs);
				if (null == sbvos || sbvos.isEmpty()) {
					// ss is multi , sblist from tc is null
					return sr;
				} else if (sbvos.size() == 1) {
					// ss is multi , unique sb
					SbVO sbvo = sbvos.get(0);
					sr.setSb(sbvo.getSbrand());

					if (StandardizeResources.sbssMapping.containsKey(sbvo)) {
						ssvos = getIntersectionSsvos(ssvos,
								StandardizeResources.sbssMapping.get(sbvo));
						if (null != ssvos && ssvos.size() == 1) {
							sr.setSs(ssvos.get(0).getSeries());
						}
					}
					return sr;

				} else {
					// ss is multi , sb is multi
					return sr;
				}
			}
		}
		return sr;
	}

	private List<SsVO> getIntersectionSsvos(List<SsVO> ss1, List<SsVO> ss2) {
		List<SsVO> ssvos = new ArrayList<>();
		Map<String, SsVO> m1 = new HashMap<>();
		for (SsVO ssVO : ss1) {
			m1.put(ssVO.getSeries(), ssVO);
		}
		for (SsVO ssVO : ss2) {
			if (m1.containsKey(ssVO.getSeries())) {
				ssvos.add(ssVO);
			}
		}
		return ssvos;
	}

	private List<String> getNbListBySbList(List<SbVO> sbvos) {
		List<String> nbs = new ArrayList<>();
		for (SbVO sbvo : sbvos) {
			if (StandardizeResources.sbnbMapping.containsKey(sbvo)) {
				nbs.addAll(StandardizeResources.sbnbMapping.get(sbvo));
			}
		}
		Map<String, String> m = new HashMap<>();
		for (String nb : nbs) {
			m.put(nb, nb);
		}
		return new ArrayList<>(m.keySet());
	}

	private List<String> getNbListBySsList(List<SsVO> ssvos) {
		List<String> nbs = new ArrayList<>();
		for (SsVO ssvo : ssvos) {
			if (StandardizeResources.sssbMapping.containsKey(ssvo)) {
				List<SbVO> sbvos = StandardizeResources.sssbMapping.get(ssvo);
				nbs.addAll(this.getNbListBySbList(sbvos));
			}
		}
		Map<String, String> m = new HashMap<>();
		for (String nb : nbs) {
			m.put(nb, nb);
		}
		return new ArrayList<>(m.keySet());
	}

	private List<String> getNbListBySbListSsList(List<SbVO> sbvos,
			List<SsVO> ssvos) {
		List<String> result = new ArrayList<>();
		List<String> nbsFromSbList = this.getNbListBySbList(sbvos);
		List<String> nbsFromSsList = this.getNbListBySsList(ssvos);
		Map<String, String> nbsFromSsMap = new HashMap<>();
		for (String nb : nbsFromSsList) {
			nbsFromSsMap.put(nb, nb);
		}
		for (String nb : nbsFromSbList) {
			if (nbsFromSsMap.containsKey(nb)) {
				result.add(nb);
			}
		}
		return result;
	}

	private StandardizeResult setSsFromSsvos(StandardizeResult sr,
			List<SsVO> ssvos) {
		if (null == ssvos || ssvos.isEmpty()) {
			// sb is ok , but ss is null
			return sr;
		} else if (ssvos.size() == 1) {
			// sb is ok and unique ss
			sr.setSs(ssvos.get(0).getSeries());
			return sr;
		} else {
			// sb is ok , multi ss
			return sr;
		}
	}

	private List<String> getNsListBySbList(List<SbVO> sbvos) {
		List<String> nss = new ArrayList<>();
		for (SbVO sbVO : sbvos) {
			if (StandardizeResources.sbssMapping.containsKey(sbVO)) {
				List<SsVO> ssvos = StandardizeResources.sbssMapping.get(sbVO);
				nss.addAll(this.getNsListBySsList(ssvos));
			}
		}
		Map<String, String> m = new HashMap<>();
		for (String ns : nss) {
			m.put(ns, ns);
		}
		return new ArrayList<>(m.keySet());
	}

	private List<String> getNsListBySbListSsList(List<SbVO> sbvos,
			List<SsVO> ssvos) {

		List<String> result = new ArrayList<>();

		List<String> nssFromSbList = this.getNsListBySbList(sbvos);
		List<String> nssFromSsList = this.getNsListBySsList(ssvos);

		Map<String, String> nssFromSsMap = new HashMap<>();

		for (String ns : nssFromSbList) {
			nssFromSsMap.put(ns, ns);
		}

		for (String ns : nssFromSsList) {
			if (nssFromSsMap.containsKey(ns)) {
				result.add(ns);
			}
		}
		return result;

	}

	private List<String> getNsListBySsList(List<SsVO> ssvos) {
		List<String> nss = new ArrayList<>();

		for (SsVO ssvo : ssvos) {
			if (StandardizeResources.ssnsMapping.containsKey(ssvo)) {
				nss.addAll(StandardizeResources.ssnsMapping.get(ssvo));
			}
		}

		Map<String, String> m = new HashMap<>();
		for (String ns : nss) {
			m.put(ns, ns);
		}
		return new ArrayList<>(m.keySet());
	}

}
